package com.kizuki.thread;

import org.junit.Test;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 *  循环会耗时
 *  一次只能保证一个共享变量的原子性
 *  ABA问题(线程修改A->B->A 别的线程compareAndSet还是成功)
 */
public class TestCAS {

    @Test
    public void testCAS(){
        AtomicInteger atomicInteger = new AtomicInteger(2020);

        System.out.println(atomicInteger.compareAndSet(2020, 2021)); // true
        System.out.println(atomicInteger.get()); // 2021

        System.out.println(atomicInteger.compareAndSet(2020, 2021)); // false
        System.out.println(atomicInteger.get()); // 2021
    }

    @Test
    public void testABA() throws InterruptedException {
        AtomicReference<String> atomicReference = new AtomicReference<>("A");

        System.out.println("main " + atomicReference.compareAndSet("A", "B"));
        System.out.println("main " + atomicReference.compareAndSet("B", "A"));

        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(4);
                System.out.println("other " + atomicReference.compareAndSet("A", "C")); // true
                System.out.println(atomicReference.get());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();

        TimeUnit.SECONDS.sleep(4);
    }

    /**
     * 锁的一种宏观分类方式是悲观锁和乐观锁。
     * 悲观锁与乐观锁并不是特指某个锁（Java中没有哪个Lock实现类就叫PessimisticLock或OptimisticLock），而是在并发情况下的两种不同策略。
     * 悲观锁（Pessimistic Lock）, 就是很悲观，每次去拿数据的时候都认为别人会修改。所以每次在拿数据的时候都会上锁。这样别人想拿数据就被挡住，直到悲观锁被释放。
     * 乐观锁（Optimistic Lock）, 就是很乐观，每次去拿数据的时候都认为别人不会修改。所以不会上锁，不会上锁！
     * 但是如果想要更新数据，则会在更新前检查在读取至更新这段时间别人有没有修改过这个数据。
     * 如果修改过，则重新读取，再次尝试更新，循环上述步骤直到更新成功（当然也允许更新失败的线程放弃操作）。
     *
     * 悲观锁阻塞事务，乐观锁回滚重试，它们各有优缺点，不要认为一种一定好于另一种。
     * 像乐观锁适用于写比较少的情况下，即冲突真的很少发生的时候，这样可以省去锁的开销，加大了系统的整个吞吐量。
     * 但如果经常产生冲突，上层应用会不断的进行重试，这样反倒是降低了性能，所以这种情况下用悲观锁就比较合适。
     */
    @Test
    public void testOptimisticLock() throws InterruptedException {
        // 乐观锁策略 本质不是锁，而是循环尝试CAS的算法
        AtomicStampedReference<String> atomicReference = new AtomicStampedReference<>("A",  1); // 带版本号的atomic

        new Thread(()->{
            System.out.println("a1 ->" + atomicReference.getStamp());

            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("a " + atomicReference.compareAndSet("A", "B", atomicReference.getStamp(), atomicReference.getStamp() + 1));
            System.out.println("a2 ->" + atomicReference.getStamp());
            System.out.println("a " + atomicReference.compareAndSet("B", "A", atomicReference.getStamp(), atomicReference.getStamp() + 1));
            System.out.println("a3 ->" + atomicReference.getStamp());
        }, "a").start();

        new Thread(()->{
            System.out.println("b1 ->" + atomicReference.getStamp());

            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            System.out.println("b " + atomicReference.compareAndSet("A", "C", atomicReference.getStamp(), atomicReference.getStamp() + 1));
            System.out.println("b2 ->" + atomicReference.getStamp());
        }, "b").start();

        TimeUnit.SECONDS.sleep(4);
    }

    /**
     * synchronized锁升级：偏向锁 → 轻量级锁 → 重量级锁
     * 偏向锁 单个线程使用时记录这个线程，第二个线程进来锁竞争时判断记录线程是否终止，否则升级锁
     * 轻量级锁 自旋锁，此时进程状态还是运行 当自旋一定次数升级锁
     * 重量级锁 线程挂起，有线程调度
     */

}
